8. The Service Layer

As well as the Web layer, Grails defines the notion of a service layer. The Grails team discourages the embedding of core application logic inside controllers, as it does not promote re-use and a clean separation of concerns.

Services in Grails are seen as the place to put the majority of the logic in your application, leaving controllers responsible for handling request flow via redirects and so on.

Creating a Service

You can create a Grails service by running the create-service command from the root of your project in a terminal window:

grails create-service simple

The above example will create a service at the location grails-app/services/SimpleService.groovy. A service's name ends with the convention Service, other than that a service is a plain Groovy class:

class SimpleService {	
}

8.1 Declarative Transactions

Services are typically involved with co-ordinating logic between domain classes, and hence often involved with persistence that spans large operations. Given the nature of services they frequently require transactional behaviour. You can of course use programmatic transactions with the withTransaction method, however this is repetitive and doesn't fully leverage the power of Spring's underlying transaction abstraction.

Services allow the enablement of transaction demarcation, which is essentially a declarative way of saying all methods within this service are to be made transactional. All services have transaction demarcation enabled by default - to disable it, simply set the transactional property to false:

class CountryService {
    static transactional = false
}

You may also set this property to true in case the default changes in the future, or simply to make it clear that the service is intentionally transactional.

Warning: dependency injection is the only way that declarative transactions work. You will not get a transactional service if you use the new operator such as new BookService()

The result is all methods are wrapped in a transaction and automatic rollback occurs if an exception is thrown in the body of one of the methods. The propagation level of the transaction is by default set to PROPAGATION_REQUIRED.

8.2 Scoped Services

By default, access to service methods is not synchronised, so nothing prevents concurrent execution of those functions. In fact, because the service is a singleton and may be used concurrently, you should be very careful about storing state in a service. Or take the easy (and better) road and never store state in a service.

You can change this behaviour by placing a service in a particular scope. The supported scopes are:

If your service is flash, flow or conversation scoped it will need to implement java.io.Serializable and can only be used in the context of a Web Flow

To enable one of the scopes, add a static scope property to your class whose value is one of the above:

static scope = "flow"

8.3 Dependency Injection and Services

Dependency Injection Basics

A key aspect of Grails services is the ability to take advantage of the Spring Framework's dependency injection capability. Grails supports "dependency injection by convention". In other words, you can use the property name representation of the class name of a service, to automatically inject them into controllers, tag libraries, and so on.

As an example, given a service called BookService, if you place a property called bookService within a controller as follows:

class BookController {
   def bookService
   …
}

In this case, the Spring container will automatically inject an instance of that service based on its configured scope. All dependency injection is done by name; Grails does not support typed injection. You can also specify the type as follows:

class AuthorService {
	BookService bookService
}

However, this has an adverse effect on reloading with an error thrown if the BookService changes in development mode.

Dependency Injection and Services

You can inject services in other services with the same technique. Say you had an AuthorService that needed to use the BookService, declaring the AuthorService as follows would allow that:

class AuthorService {
	def bookService
}

Dependency Injection and Domain Classes

You can even inject services into domain classes, which can aid in the development of rich domain models:

class Book {	
	…
	def bookService
	def buyBook() {
		bookService.buyBook(this)
	}
}

8.4 Using Services from Java

One of the powerful things about services is that since they encapsulate re-usable logic, you can use them from other classes, including Java classes. There are a couple of ways you can re-use a service from Java. The simplest way is to move your service into a package within the grails-app/services directory. The reason this is a critical step is that it is not possible to import classes into Java from the default package (the package used when no package declaration is present). So for example the BookService below cannot be used from Java as it stands:

class BookService {
	void buyBook(Book book) {
		// logic
	}
}

However, this can be rectified by placing this class in a package, by moving the class into a sub directory such as grails-app/services/bookstore and then modifying the package declaration:

package bookstore
class BookService {
	void buyBook(Book book) {
		// logic
	}
}

An alternative to packages is to instead have an interface within a package that the service implements:

package bookstore;
interface BookStore {
	void buyBook(Book book);
}

And then the service:

class BookService implements bookstore.BookStore {
	void buyBook(Book b) {
		// logic
	}
}

This latter technique is arguably cleaner, as the Java side only has a reference to the interface and not to the implementation class. Either way, the goal of this exercise to enable Java to statically resolve the class (or interface) to use, at compile time. Now that this is done you can create a Java class within the src/java package, and provide a setter that uses the type and the name of the bean in Spring:

package bookstore;
// note: this is Java class
public class BookConsumer {
	private BookStore store;

public void setBookStore(BookStore storeInstance) { this.store = storeInstance; } … }

Once this is done you can configure the Java class as a Spring bean in grails-app/conf/spring/resources.xml (For more information one this see the section on Grails and Spring):

<bean id="bookConsumer" class="bookstore.BookConsumer">
	<property name="bookStore" ref="bookService" />
</bean>